from datetime import datetime
from flask_wtf.csrf import generate_csrf
from info.models import User
from . import passport_bp
from flask import request, current_app, abort, make_response, session, render_template, jsonify
from info.utils.captcha.captcha import captcha
from info import redis_store, constants, db
import re
from info.lib.yuntongxun.sms import CCP
from info.utils.response_code import RET


# 2.使用蓝图
# 127.0.0.1:5000/passport/image_code?code_id=UUID
@passport_bp.route('/image_code')
def get_image_code():
    """获取验证码图片"""
    """
    1.获取参数
        1.1 获取code_id:唯一编码UUID
    2.校验参数
        2.1 判断code_id是否有值
    3.逻辑处理
        3.1 生成验证码图片 & 生成图片对应的真实值
        3.2 根据code_id编号为key,真实值为value的数据存储到redis数据库中
    4.返回值
        4.1将生成的验证码图片返回给前端
    """
    # 1.1获取code_id
    code_id = request.args.get("code_id")
    # 2.1判断code_id是否有值
    if not code_id:
        current_app.logger.error("参数不足")
        abort(403)
    # 3.1生成验证码图片 & 生成图片对应的真实值
    image_name, image_code, image_data = captcha.generate_captcha()
    # 3.2根据code_id编号为key, 真实值为value的数据存储到redis数据库中
    try:
        redis_store.setex(code_id, constants.IMAGE_CODE_REDIS_EXPIRES, image_code)
    except Exception as e:
        # 记录日志
        current_app.logger.error(e)
        abort(500)
    # 4.1将生成的验证码图片返回给前端
    response = make_response(image_data)
    response.headers["Content-Type"] = "image/JPEG"
    return response


# 127.0.0.1:5000/passport/sms_code
@passport_bp.route('/sms_code', methods=['POST'])
def send_smscode():
    """发送短信验证码 后端接口"""
    """
    1.获取参数
        1.1 mobile:手机号码 image_code_id:uuid唯一编码  image_code:用户填写的图片验证码
    2.校验参数
        2.1 非空判断
        2.2 手机号正则校验格式
    TODO 查询手机号码是否注册过(提高用户体验)
    3.逻辑处理
        3.1 从redis中取出image_code_id对应的真实值
            3.1.1 数据库中有对应的值real_code_id: 从数据库中删除(防止多次用这个进行值校验)
            3.1.2 没有对应的值: 图片验证码过期
        3.2.判断image_code和真实值是否一致
            3.2.1 相等: 发送短信验证码
                1. 自己随机生成6位短信验证码
                2. 调用ccp工具类发送
                3. 发送成功之后,保存短信验证码到redis数据库中
            3.2.2 不相等: 告诉前端,图片验证码错误(前端再次生成一张图片)
    4.返回值
        4.1 发送短信验证码成功
    """
    # import json
    # json.loads(request.data)
    # 1 获取参数
    # 提取前端发送的json数据格式,并且将数据转换成python对象
    mobile = request.json.get("mobile")
    image_code = request.json.get("image_code")
    image_code_id = request.json.get("image_code_id")
    # 2.1 参数非空判断
    if not all([mobile, image_code, image_code_id]):
        current_app.logger.error("参数错误")
        # return jsonify({"errno": RET.PARAMERR, "errmsg": "参数错误"})
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    # 2.2判断手机号格式是否正确
    if not re.match("1[35789]\d{9}", mobile):
        current_app.logger.error("手机格式有误")
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式有误")
    # TODO 查询手机号码是否注册过
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户信息异常")
    if user:
        # 用户已经注册
        return jsonify(errno=RET.DATAEXIST, errmsg="该用户已注册")
    # 3.1 从redis中取出image_code_id对应的真实值
    try:
        real_code_id = redis_store.get(image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询验证码真实值异常")
    if real_code_id:
        try:
            redis_store.delete(image_code_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="删除验证码真实值异常")
    else:
        current_app.logger.error("图片验证码真实值过期")
        return jsonify(errno=RET.NODATA, errmsg="图片验证码真实值过期")
    # 3.2 判断image_code和真实值是否一致
    """
    细节:
        1.忽略大小写
        2.real_image_id如果直接获取是二进制形式  设置decode_response=True
    """
    # 3.2.1 相等: 发送短信验证码
    if real_code_id.lower() == image_code.lower():
        import random
        sms_code = random.randint(100000, 999999)
        """
        参数1: 手机号码
        参数2: {6位的短信验证码, 分钟}
        参数3: 模板id: 选用第一个模板
        """
        try:
            result = CCP().send_template_sms(mobile, {sms_code, "%d" % (constants.SMS_CODE_REDIS_EXPIRES/60)}, 1)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.THIRDERR, errmsg="短信验证码发送失败")
        if result == -1:
            current_app.logger.error("短信验证码发送失败")
            return jsonify(errno=RET.THIRDERR, errmsg="短信验证码发送失败")
        # 发送成功之后,保存短信验证码到redis数据库中
        print(sms_code)
        try:
            redis_store.setex(mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.THIRDERR, errmsg="保存短信验证码异常")
    # 3.2.2 不相等: 告诉前端,图片验证码错误(前端再次生成一张图片)
    else:
        current_app.logger.error("填写图片验证码有误")
        return jsonify(errno=RET.DATAERR, errmsg="填写图片验证码有误")
    # 4.返回值给前端
    return jsonify(errno=RET.OK, errmsg="短信验证码发送成功")


@passport_bp.route('/register', methods=['post'])
def register():
    """注册后端接口"""
    """
    1.获取参数
        1.1 手机号:mobile  短信验证码:sms_code  密码:password
    2.校验参数
        2.1 非空判断
        2.2 校验手机号码格式
    3.逻辑处理
        3.1 从redis中取出key为mobile对应的验证码
            有值: 从数据库中删除
            没有值: 短信验证码过期
        3.2 对real_sms_code和sms_code进行比较
            相等: 创建用户对象,将用户对象存入redis中
            不想等: 提示前端
        3.3 注册成功,一般表示登陆成功,使用session保存用户信息
    4.返回结果
    """
    # 1.获取参数
    mobile = request.json.get("mobile")
    sms_code = request.json.get("sms_code")
    password = request.json.get("password")
    # 2.1 非空判断
    if not all([mobile, sms_code, password]):
        current_app.logger.error("参数不齐")
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    # 2.2手机号码格式校验
    if not re.match("1[35789]\d{9}", mobile):
        current_app.logger.error("手机格式有误")
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式有误")
    # 3.1 从redis中取出key为mobile对应的验证码
    try:
        real_sms_code = redis_store.get(mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询短信验证码异常")
    # 有值:
    if real_sms_code:
        redis_store.delete(mobile)
    # 没有值:
    else:
        current_app.logger.error("短信验证码过期")
        return jsonify(errno=RET.NODATA, errmsg="短信验证码过期")
    # 3.2 对real_sms_code和sms_code进行比较
    if real_sms_code != sms_code:
        current_app.logger.error("填写短信验证码有误")
        return jsonify(errno=RET.DATAERR, errmsg="填写短信验证码有误")
    # 3.2.2 验证码正确: 创建用户对象,将用户对象存入redis中
    user = User()
    user.nick_name = mobile
    user.mobile = mobile
    print("xxx")
    # TODO: 密码未加密
    # user.password_hash = password
    # 方法1: 在User表中定义加密的方法
    # user.set_password_hash(password)
    # 方法2: 使用属性的set和get方法进行加密
    user.password = password
    # 记录最后一次登陆时间
    user.last_login = datetime.now()
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 数据库回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户对象异常")
    # 3.3 注册成功,一般表示登陆成功,使用session保存用户信息
    session["user_id"] = user.id
    session["mobile"] = mobile
    session["nick_name"] = mobile
    session["is_admin"] = user.is_admin
    # 4.返回结果: 注册成功
    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_bp.route('/login', methods=['post'])
def login():
    """登陆后端接口"""
    """
    1.获取参数
        1.1 手机号:mobile  密码:password
    2.参数校验
        2.1 非空判断
        2.2 手机号码格式校验
    3.逻辑处理
        3.1 从数据库中查询mobile对应的user
            3.1.1 无值: 用户不存在
            3.1.2 有值: 将password进行hash加密
        3.2 比较password和real_password_hash
            3.2.1 相等: 用session保存用户信息
            3.2.2 不相等: 账户名或密码错误
    4.返回结果
    """
    # 1 获取参数
    mobile = request.json.get("mobile")
    password = request.json.get("password")
    # 2.1 非空判断
    if not all([mobile, password]):
        current_app.logger.error("参数不齐")
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    # 2.2 手机号码格式校验
    if not re.match("1[35789]\d{9}", mobile):
        current_app.logger.error("手机格式有误")
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式有误")
    # 3.1 从数据库中查询mobile对应的user
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="真实密码查询异常")
    # 3.1.1 无值: 请先注册
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="该用户未注册")
    # 3.2 比较password和real_password_hash
    if not user.check_passowrd(password):
        return jsonify(errno=RET.DATAERR, errmsg="密码错误")
    # 3.2.2 相等：记录用户登陆信息, 修改最后一次登陆时间
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    session["is_admin"] = user.is_admin
    user.last_login = datetime.now()
    try:
        # 提交对象的修改操作到数据库
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 数据库回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户数据异常")
    # 4. 返回结果
    return jsonify(errno=RET.OK, errmsg="登陆成功")


@passport_bp.route('/login_out')
def login_out():
    """登出功能后端接口"""
    """
    1.删除保存到服务器的session信息
    """
    session.pop("user_id", None)
    session.pop("mobile", None)
    session.pop("nick_name", None)
    session.pop("is_admin", None)
    return jsonify(errno=RET.OK, errmsg="登出成功")







